home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / tnos / tnos100s / bmutil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-16  |  43.8 KB  |  1,747 lines

  1. /*
  2.  *    Simple mail user interface for KA9Q IP/TCP package.
  3.  *    A.D. Barksdale Garbee II, aka Bdale, N3EUA
  4.  *    Copyright 1986 Bdale Garbee, All Rights Reserved.
  5.  *    Permission granted for non-commercial copying and use, provided
  6.  *    this notice is retained.
  7.  *    Copyright 1987 1988 Dave Trulli NN2Z, All Rights Reserved.
  8.  *    Permission granted for non-commercial copying and use, provided
  9.  *    this notice is retained.
  10.  *
  11.  *    Ported to NOS at 900120 by Anders Klemets SM0RGV.
  12.  *
  13.  *  Userlogging, 'RM' and 'KM' implementation,
  14.  *  more-prompts for all types
  15.  *  920307 and later, by Johan. K. Reinalda, WG7J/PA3DIS
  16.  *  920719 and later, by Brian A. Lantz, KO4KS
  17.  */
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <ctype.h>
  21. #include <time.h>
  22. #include <io.h>
  23. #include "global.h"
  24. #include "config.h"
  25. #include "ftpserv.h"
  26. #include "smtp.h"
  27. #include "proc.h"
  28. #include "usock.h"
  29. #include "socket.h"
  30. #include "telnet.h"
  31. #include "timer.h"
  32. #include "session.h"
  33. #include "files.h"
  34. #include "mailbox.h"
  35. #include "cmdparse.h"
  36. #include "bm.h"
  37. #include "mailutil.h"
  38.  
  39. #define        SETVBUF
  40. #if    defined(UNIX) || defined(MICROSOFT)
  41. #include    <sys/types.h>
  42. #endif
  43. /*
  44. #if    defined(UNIX) || defined(MICROSOFT) || defined(__TURBOC__)
  45. #include    <sys/stat.h>
  46. #endif
  47. #ifdef AZTEC
  48. #include <stat.h>
  49. #endif
  50. */
  51. #include <fcntl.h>
  52. #include "bm.h"
  53. #include "mailbox.h"
  54.  
  55. #ifdef SETVBUF
  56. #define        MYBUF    1024
  57. #endif
  58.  
  59. extern long ftell();
  60. extern int makeBBSbid __ARGS((char *bid, char *line, int ours));
  61. extern char DFAR *cb_lookname __ARGS((char *));
  62. extern char *skipwhite __ARGS((char *ptr));
  63. extern char *skipnonwhite __ARGS((char *ptr));
  64. char Badmsg[] = "Invalid Message number %d\n";
  65. char Nomail[] = "No messages\n";
  66. static char NoMsgs[] = "No message numbers given\n";
  67. static char anymore[] = "More(N=no)? ";
  68. static char Noaccess[] = "Unable to access %s\n";
  69. static int readnotes __ARGS((struct mbx *m, int already));
  70. static long isnewmail __ARGS((struct mbx *m));
  71. static int initnotes __ARGS((struct mbx *m));
  72. int lockit __ARGS((struct mbx *m));
  73. long fsize __ARGS((char *name));
  74. static void mfclose __ARGS((struct mbx *m));
  75. extern char *nntp_name_expansion __ARGS((char *name));
  76.  
  77.  
  78. FILE *
  79. subdir_fopen (name, mode)
  80. char *name, *mode;
  81. {
  82. FILE *fp;
  83. char *cp;
  84.  
  85. #define ENOENT 2
  86.  
  87.     if ((fp = fopen (name, mode)) != (FILE *)0)
  88.         return fp;
  89.     if ((errno == ENOENT) && !strchr (mode, 'r'))    {
  90.         cp = &name[1];
  91.         while ((cp = strpbrk (cp, "/\\")) != (char *)0)    {
  92.             *cp = 0;
  93.             if (access (name, 0))
  94.                 if (mkdir (name))
  95.                     return ((FILE *) 0);
  96.             *cp++ = '/';
  97.         }
  98.         fp = fopen (name, mode);
  99.         return fp;    /* NULL if STILL not opened */
  100.     }
  101.     return ((FILE *) 0);
  102. }
  103.  
  104.  
  105. #ifdef MAILBOX
  106.  
  107. static int
  108. initnotes(m)
  109. struct mbx *m;
  110. {
  111. register struct    let *cmsg;
  112. char buf[256];
  113. int     i, ret;
  114. int wasit = 0;
  115.  
  116.     if (m->mfile != NULLFILE)    {
  117.         fclose (m->mfile);
  118.         wasit = m->nmsgs;
  119.     }
  120.     sprintf(buf,"%s/%s.txt",Mailspool,m->area);
  121.     if ((m->mfile = subdir_fopen(buf,READ_TEXT)) == NULLFILE)
  122.         return 0;
  123.     m->mboxsize = filelength (fileno(m->mfile));
  124.     if(!(m->sid & MBX_SID) && !stricmp(m->area,m->name)) /* our private mail area */
  125.         m->mysize = m->mboxsize;
  126.     m->nmsgs = 0;
  127.     m->change = 0;
  128.     m->newmsgs = 0;
  129.     m->anyread = 0;
  130.     /* Allocate space for reading messages */
  131.     readnotes(m, wasit);
  132. #ifdef USERLOG
  133.     m->current = 0; /*reset it*/
  134.     if (m->nmsgs)
  135.         for (cmsg = &m->mbox[1],i = 1; i <= m->nmsgs; i++, cmsg++)
  136.             if ((cmsg->status & BM_READ) == 0) {
  137.                 m->current = i; /* first new message */
  138.                 break;
  139.             }
  140.  
  141.     /* start at one if no new messages */
  142.     if ((m->current == 0) && m->nmsgs)
  143.         m->current++;
  144. #endif
  145.     return 0;
  146. }
  147.  
  148. /* readnotes assumes that ifile is pointing to the first
  149.  * message that needs to be read.  For initial reads of a
  150.  * notesfile, this will be the beginning of the file.  For
  151.  * rereads when new mail arrives, it will be the first new
  152.  * message.
  153.  */
  154. static int
  155. readnotes(m, already)
  156. register struct mbx *m;
  157. int already;
  158. {
  159. register FILE *fp;
  160. char mailbox[100];
  161. long size;
  162. register int i, isit;
  163. register struct    let *cmsg;
  164. struct let *new;
  165.  
  166.     m->newmsgs = m->hmsgs = 0;
  167.     sprintf(mailbox,"%s/CONTROL/%s.ctl",Mailspool,m->area);
  168.     if((fp = subdir_fopen(mailbox,READ_BINARY)) != NULLFILE){
  169. #ifndef TNOS_68K
  170.         m->stdoutbuf = mallocw(MYBUF);
  171.         setvbuf(fp, m->stdoutbuf, _IOFBF, MYBUF);
  172. #endif
  173.         size = filelength (fileno(fp));
  174. /*        fseek (fp, 0L, 2);
  175.         size = ftell (fp);
  176.         fseek (fp, 0L, 0);    */
  177.         new = (struct let *) malloc (size + sizeof(struct let));
  178.         fread (&new[1], size, 1, fp);
  179.         fclose(fp);
  180.         if (already && !strcmp (m->area, m->name))
  181.             memcpy (new, m->mbox, (already + 1) * sizeof(struct let));
  182. #ifndef TNOS_68K
  183.         free (m->stdoutbuf);
  184.         m->stdoutbuf = 0;
  185. #endif
  186.         free (m->mbox);
  187.         m->mbox = new;
  188.         m->nmsgs = (int) (size / (long) sizeof(struct let));
  189.         isit = issysarea (m->area);
  190.         for (cmsg = &m->mbox[1],i = 1; i <= m->nmsgs; i++, cmsg++)
  191.             if (isit)    {
  192.                 if ((cmsg->bid > m->lastread) && !(cmsg->status & BM_DELETE))
  193.                     m->newmsgs++;
  194.                 else
  195.                     cmsg->status |= BM_READ;
  196.                 if (cmsg->status & BM_ONHOLD)
  197.                     m->hmsgs++;
  198.             } else if (!(cmsg->status & BM_READ))
  199.                 m->newmsgs++;
  200.     }
  201.     else
  202.         m->nmsgs = 0;
  203.     return 0;
  204. }
  205.  
  206. /* list headers of a notesfile a message */
  207. /* Rearranged display - WG7J */
  208. int
  209. dolistnotes(argc,argv,p)
  210. int argc;
  211. char *argv[];
  212. void *p;
  213. {
  214. struct mbx *m;
  215. struct    let *cmsg;
  216. char    *cp, *s;
  217. char smtp_date[SLINELEN], smtp_from[SLINELEN];
  218. char smtp_subject[SLINELEN], tstring[LINELEN], type;
  219. char smtp_to[SLINELEN], smtp_bid[30];
  220. int start, stop;
  221. long    size;
  222. char    *area;
  223. int c,usemore=0,lin;
  224. #ifdef USERLOG
  225. long msgid;
  226. #endif
  227.  
  228.     m = (struct mbx *) p;
  229.  
  230.     /*If this user doesn't have read-permissions,
  231.      *we're not going to let him list anything - WG7J    */
  232.     if(m->privs & NO_READCMD)     {
  233.         tputs(Noperm);
  234.         return 0;
  235.     }
  236.  
  237.     if (m->mfile == NULLFILE) {
  238.         tputs(Nomail);
  239.         return 0;
  240.     }
  241.     if((m->stype == 'S' || m->stype == '$' || m->stype == '>' || m->stype == '<') && argc == 1) {
  242.         tputs("Search criterium needed!\n");
  243.         return 0;
  244.     }
  245.  
  246.     if((lin=m->morerows) != 0)
  247.         usemore = 1;    /* Display More prompt */
  248.  
  249.     area = strdup(m->area);
  250.     while((cp = strchr(area,'/')) != NULLCHAR)
  251.         *cp = '.';
  252.     bbscolorcls (m);
  253.     tprintf("Mail area: ");
  254.     bbscolorchange (m, "09");
  255.     tprintf (area);
  256.     bbscolorchange (m, "0B");
  257.     tprintf ("  %d ", m->nmsgs);
  258.     bbscolorchange (m, "0F");
  259.     tprintf ("message%s -  ",m->nmsgs == 1 ? " " : "s ");
  260.     bbscolorchange (m, "0B");
  261.     tprintf ("%d ", m->newmsgs);
  262.     bbscolorchange (m, "0F");
  263.     tprintf ("new\n\nStat  #        TO         FROM  DATE    SIZE SUBJECT\n");
  264.     free(area);
  265.  
  266.     stop = m->nmsgs;
  267.     if(m->stype == 'L') {        /* LL (List Latest) command */
  268.         if(argc > 1)
  269.             start = stop - atoi(argv[1]) + 1;
  270.         else
  271.             start = stop;
  272.         if (start < 1)
  273.             start = 1;
  274.     } else {
  275.         if((m->stype == 'A') || (m->stype == '>') || (m->stype == '<') || (m->stype == 'S') || (m->stype == '$') || (m->stype == 'H')) {
  276.             start = 1;
  277.             stop = m->nmsgs;
  278.         } else {
  279.             if(argc > 1)
  280.                 start = atoi(argv[1]);
  281.             else
  282. #ifdef USERLOG
  283.                 start = m->nmsgs - m->newmsgs + 1;
  284. #else
  285.                 start = 1;
  286. #endif
  287.             if(argc > 2)
  288.                 stop = atoi(argv[2]);
  289.         }
  290.     }
  291.     if(stop > m->nmsgs)
  292.         stop = m->nmsgs;
  293.     if(start < 1 || start > stop)     {
  294.         if((m->stype == ' ') || (m->stype == 'M'))
  295.             tputs("No new mail.\n");
  296.         else
  297.             tputs("Invalid range.\n");
  298.         return 0;
  299.     }
  300.     for (cmsg = &m->mbox[start]; start <= stop; start++, cmsg++) {
  301.         *smtp_date = '\0';
  302.         *smtp_from = '\0';
  303.         *smtp_subject = '\0';
  304.         *smtp_to = '\0';
  305.         *smtp_bid = '\0';
  306.         type = ' ';
  307.         fseek(m->mfile,cmsg->start,0);
  308.         size = cmsg->size;
  309.         /* Be a little less selfish - WG7J */
  310.         pwait(NULL);
  311.  
  312. #ifdef USERLOG
  313. /* We need to get the id from the last message listed !
  314.  * m->mbox[i].start (ie cmsg->start) points to the 'From ' line
  315.  * next are the 'Received....' and 'ID...' lines
  316.  * These are thes line added by our smtp server.
  317.  * following the 'AA' is the number that we want ! - WG7J
  318.  */
  319.         if(start == stop) {
  320.             /*The 'From ' line*/
  321.             fgets(tstring,sizeof(tstring),m->mfile);
  322.             size -= strlen(tstring);
  323.             /*The 'Received' line*/
  324.             fgets(tstring,sizeof(tstring),m->mfile);
  325.             size -= strlen(tstring);
  326.             /*The 'ID' line*/
  327.             fgets(tstring,sizeof(tstring),m->mfile);
  328.             size -= strlen(tstring);
  329.             /* find id number */
  330. #ifdef nope
  331.             if((cmsg->bid > m->lastread) && (cmsg->bid > m->newlastread))
  332.                 m->newlastread = cmsg->bid;            
  333. #else
  334.             if((cp=strstr(tstring,"AA")) != NULLCHAR) {
  335.                 /*what follows is the message-number*/
  336.                 msgid = atol(cp+2);
  337.                 if((msgid > m->lastread) && (msgid > m->newlastread))
  338.                     m->newlastread = msgid;
  339.             }
  340. #endif
  341.         }
  342. #endif
  343.  
  344.         while (size > 0 && fgets(tstring,sizeof(tstring),m->mfile) != NULLCHAR) {
  345.             pwait(NULL);
  346.             if (*tstring == '\n')   /* end of header */
  347.                 break;
  348.             size -= strlen(tstring);
  349.             rip(tstring);
  350.             /* handle continuation later */
  351.             if (*tstring == ' '|| *tstring == '\t')
  352.                 continue;
  353.             switch(htype(tstring)) {
  354.             case FROM:    cp = getaddress(tstring,0);
  355.                     strncpy(smtp_from,cp != NULLCHAR ? cp : "", 8);
  356.                     if((cp=strchr(smtp_from,'@')) != NULLCHAR)
  357.                         *cp = '\0';         /* get rid of @-host or @-bbs field */
  358.                     break;
  359.             case SUBJECT:    cp = skipwhite (&tstring[9]);
  360.                     strncpy (smtp_subject,cp, 35);
  361.                     break;
  362.             case MSGID:    strncpy (smtp_bid,&tstring[12], 29);
  363.                     break;
  364.             case DATE:    if ((cp = strchr(tstring,',')) == NULLCHAR)
  365.                         cp = &tstring[6];
  366.                     else
  367.                         cp++;
  368.                     /* skip spaces */
  369.                     while (*cp == ' ')     cp++;
  370.                     if(strlen(cp) < 17)
  371.                         break;     /* not a valid length */
  372.                     s = smtp_date;
  373.                     /* copy day */
  374.                     if (atoi(cp) < 10 && *cp != '0')
  375.                         *s++ = ' ';
  376.                     else
  377.                         *s++ = *cp++;
  378.                     *s++ = *cp++;
  379.  
  380.                     *s++ = ' ';
  381.                     *s = '\0';
  382.                     while (*cp == ' ')
  383.                         cp++;
  384.                     strncat(s,cp,3);    /* copy month */
  385. #ifdef use_time
  386.                             cp += 3;
  387.                     while (*cp == ' ')
  388.                         cp++;
  389.                     /* skip year */
  390.                     while (isdigit(*cp))
  391.                         cp++;
  392.                     /* copy time */
  393.                     strncat(s,cp,6); /* space hour : min */
  394. #endif
  395.                             break;
  396.             case BBSTYPE:    type = tstring[16];
  397.                     break;
  398.             case TO:    strncpy (smtp_to,&tstring[4], 13);
  399.                     break;
  400.             case NOHEADER:    break;
  401.             }
  402.         }
  403.         if(m->stype == ' ' || m->stype == 'L' || m->stype == 'M' ||
  404.             m->stype == 'A' || (type == m->stype && m->stype != ' ') ||
  405.             ((m->stype == 'H') && (cmsg->status & BM_ONHOLD)) ||
  406.             ((m->stype == 'S') && (strstr(strlwr(smtp_subject),argv[1]) != NULLCHAR)) ||
  407.             ((m->stype == '<') && (strstr(strlwr(smtp_from),argv[1]) != NULLCHAR)) ||
  408.             ((m->stype == '$') && (strstr(strlwr(smtp_bid),argv[1]) != NULLCHAR)) ||
  409.             ((m->stype == '>') && (strstr(strlwr(smtp_to),argv[1]) != NULLCHAR)))         {
  410.                 lin--;
  411.                 if ((cmsg->status & BM_DELETE) && !(m->privs & SYSOP_CMD))
  412.                     strcpy (smtp_subject, "[DELETED]");
  413.                 bbscolorchange (m, "07");
  414.                 tprintf("%c%c%c%c%3d ",
  415.                     (start == m->current ? '>' : ' '),
  416.                     (cmsg->status & BM_ONHOLD ? 'H' :
  417.                     cmsg->status & BM_DELETE ? 'D' : ' '),
  418.                     (cmsg->status & BM_READ ? 'Y' : 'N'),
  419.                     (cmsg->status & BM_PERMANENT) ? 'P' : ' ',
  420.                     start);
  421.                 bbscolorchange (m, "0E");
  422.                 tprintf("%13.13s ",smtp_to);
  423.                 bbscolorchange (m, "0C");
  424.                 tprintf("%8.8s ",smtp_from);
  425.                 bbscolorchange (m, "07");
  426.                 tprintf("%-7.7s ",smtp_date);
  427.                 tprintf("%5ld ",cmsg->size);
  428.                 bbscolorchange (m, "0E");
  429.                 tprintf("%.34s\n", smtp_subject);
  430.         }
  431.         /* More prompting added - WG7J */
  432.         if(usemore && lin == 0)        {
  433.             if(m->type == TELNET || m->type == TIP)
  434.                 c = tkeywait("--More--",0);
  435.             else  /* For AX.25 and NET/ROM connects - WG7J */
  436.                 c = mykeywait(anymore, m);
  437. #ifdef MBXTDISC
  438.             start_timer(&m->tdisc);
  439. #endif
  440.             if(c == -1 || c == 'q' || c == 'Q' || c == 'n' || c == 'N')
  441.                 break;
  442. #ifdef TNOS_68K
  443.             if(c == '\l' || c == '\r')
  444. #else
  445.             if(c == '\n' || c == '\r')
  446. #endif
  447.                 lin = 1;
  448.             else
  449.                 lin = m->morerows;
  450.  
  451.         }
  452.     }
  453.     return 0;
  454. }
  455.  
  456. /*  save msg on stream - if noheader set don't output the header */
  457. int
  458. msgtofile(m,msg,tfile,noheader)
  459. struct mbx *m;
  460. int msg;
  461. FILE *tfile;   /* already open for write */
  462. int noheader;
  463. {
  464.     char    tstring[LINELEN];
  465.     long     size;
  466.  
  467.     if (m->mfile == NULLFILE) {
  468.         tprintf(Nomail);
  469.         return -1;
  470.     }
  471.     fseek(m->mfile,m->mbox[msg].start,0);
  472.     size = m->mbox[msg].size;
  473.  
  474.     if (noheader) {
  475.         /* skip header */
  476.         while (size > 0 && fgets(tstring,sizeof(tstring),m->mfile)
  477.                != NULLCHAR) {
  478.             size -= strlen(tstring);
  479.             pwait (NULL);
  480.             if (*tstring == '\n')
  481.                 break;
  482.         }
  483.     }
  484.     while (size > 0 && fgets(tstring,sizeof(tstring),m->mfile)
  485.            != NULLCHAR) {
  486.         pwait (NULL);
  487.         size -= strlen(tstring);
  488.         fputs(tstring,tfile);
  489.         if (ferror(tfile)) {
  490.             tprintf("Error writing mail file\n");
  491.             return -1;
  492.         }
  493.     }
  494.     return 0;
  495. }
  496.  
  497. void
  498. dodeleting (m, msg)
  499. struct mbx *m;
  500. int msg;
  501. {
  502.     switch (m->stype)    {
  503.         case 'U':    m->mbox[msg].status &= ~BM_DELETE;
  504.                 tprintf("Msg %d Un-Killed.\n", msg);
  505.                 break;
  506.         case 'A':    m->mbox[msg].status &= ~BM_ONHOLD;
  507.                 tprintf("Msg %d Available.\n", msg);
  508.                 break;
  509.         case 'H':    m->mbox[msg].status |= BM_ONHOLD;
  510.                 tprintf("Msg %d Held.\n", msg);
  511.                 break;
  512.         case 'T':    m->mbox[msg].status &= ~BM_PERMANENT;
  513.                 tprintf("Msg %d Temporary.\n", msg);
  514.                 break;
  515.         case 'P':    m->mbox[msg].status |= BM_PERMANENT;
  516.                 tprintf("Msg %d Permanent.\n", msg);
  517.                 break;
  518.         default :    m->mbox[msg].status |= BM_DELETE;
  519.                 tprintf("Msg %d Killed.\n", msg);
  520.                 if (!issysarea (m->area))
  521.                     m->change |= CHG_DELETE;
  522.                 break;
  523.     }
  524.     statusCtl (m->area, "ctl", &m->mbox[msg], msg, 0);
  525. }
  526.  
  527.  
  528. /* dodelmsg - delete message in current notesfile */
  529. /* Modified to allow the 'KM' command. 920307 -  WG7J */
  530. /* also handles holsing/releasing messages - KO4KS */
  531. int
  532. dodelmsg(argc,argv,p)
  533. int argc;
  534. char *argv[];
  535. void *p;
  536. {
  537.     struct mbx *m;
  538.     int msg,i;
  539.     char *myargv[NARG];
  540.     int myargc;
  541.     int maxmsg;
  542.     struct let *cmsg;
  543.     char *tmpbuf;
  544.     int start, end;
  545.  
  546.     m = (struct mbx *) p;
  547.  
  548.     /* If this user doesn't have read-permissions,
  549.      * we're not going to let him kill/hold anything;
  550.      * allow anyone to kill/hold messages in areas
  551.      * who's names start with 'nts' - WG7J
  552.      */
  553.     /* Check if we have permission to delete others mail */
  554.     if( (m->privs & NO_READCMD) || (m->privs & NO_SENDCMD) ||
  555.         ( !(m->privs & FTP_WRITE) &&
  556.             stricmp(m->area,m->name) &&
  557.             strnicmp(m->area,"nts",3)) ){
  558.         tputs(Noperm);
  559.         return 0;
  560.     }
  561.     if ((m->stype == 'A' || m->stype == 'H' || m->stype == 'P' || m->stype == 'T') && !(m->privs & SYSOP_CMD))    {
  562.         tputs(Noperm);
  563.         return 0;
  564.     }
  565.     if (m->stype == 'S')    {
  566.     myargc = msg = atoi(argv[1]);
  567.     i = atoi(argv[2]);
  568.     if ((argc < 3) || !msg || !i)
  569.             tputs(NoMsgs);
  570.     else    {
  571.         for ( ; myargc <= i; myargc++)    {
  572.             m->mbox[myargc].status |= BM_DELETE;
  573.             statusCtl (m->area, "ctl", &m->mbox[myargc], myargc, 0);
  574.         }
  575.         if (!issysarea (m->area))
  576.             m->change |= CHG_DELETE;
  577.         tprintf("Msgs %d to %d Killed.\n", msg, i);
  578.     }
  579.         return 0;
  580.     }
  581.     if (m->mfile == NULLFILE) {
  582.         tputs(Nomail);
  583.         return 0;
  584.     }
  585.     /*If this is the KM command, setup myargv[]
  586.      *to contain up to NARG message numbers - WG7J
  587.      */
  588.     if(m->stype == 'M') {
  589.         myargc = 1;
  590.         /* scan all messsages to find read ones */
  591.         maxmsg = min(m->nmsgs,NARG-1);
  592.         for(i=1;i<=maxmsg;i++){
  593.             cmsg = &m->mbox[i];
  594.             if(cmsg->status & BM_READ) { /*found a read msg!*/
  595.                 tmpbuf = mallocw(18); /*allocate space for the new argument*/
  596.                 myargv[myargc++] = itoa(i,tmpbuf,10);
  597.             }
  598.         }
  599.         if(myargc == 1) {
  600.             tputs(NoMsgs);
  601.             return 0;
  602.         }
  603.          argc = myargc;
  604.     } else {
  605.         if(argc == 1)     {
  606.             if (m->stype != 'A')    {
  607.         msg = m->current;
  608.         dodeleting (m, msg);
  609.                 return 0;
  610.             } else    {
  611.             for(i=1;i<=m->nmsgs;i++){
  612.                 cmsg = &m->mbox[i];
  613.                 if(cmsg->status & BM_ONHOLD)    {
  614.             cmsg->status &= ~BM_ONHOLD;
  615.             statusCtl (m->area, "ctl", cmsg, i, 0);
  616.             }
  617.             }
  618.         tprintf("All Messages Available.\n");
  619.         return 0;
  620.         }
  621.         }
  622.         /*simply point to the old arguments*/
  623.         for(i=1;i<argc;i++)
  624.             myargv[i] = argv[i];
  625.     }
  626.  
  627.     /* See if x - y format was used and use a for i = x to y loop */
  628.     if(myargv[2][0] == '-') {
  629.         start = atoi(myargv[1]);
  630.         end   = atoi(myargv[3]);
  631.         if(start < 0 || start > m->nmsgs || end < 0 || end > m->nmsgs ) {
  632.             tprintf(Badmsg,start);
  633.             start = end-1;
  634.         }
  635.         for(i=start;i<=end;i++) {
  636.             msg = i;
  637.         dodeleting (m, msg);
  638.         } /* endfor */
  639.     } else for(i = 1; i < argc; ++i) {
  640.         tmpbuf = strchr(myargv[i],'-'); /* N5KNX: allow from-to msg specification */
  641.         msg = atoi(myargv[i]);
  642.         if (tmpbuf == NULLCHAR)
  643.             maxmsg = msg;
  644.         else
  645.             maxmsg = atoi(++tmpbuf);
  646.         if (maxmsg < msg) {
  647.             tprintf(Badmsg,myargv[i]);
  648.             continue;
  649.         }
  650.         for (; msg <= maxmsg; msg++) {
  651.             if(msg < 0 || msg > m->nmsgs) {
  652.                 tprintf(Badmsg,msg);
  653.                 continue;
  654.             }
  655.     dodeleting (m, msg);
  656.     }
  657.     }
  658.     /* If this was 'KM'
  659.      * free the memory allocated for myargv[] - WG7J
  660.      */
  661.     if(m->stype == 'M') {
  662.         for(i=1;i<argc;i++)
  663.             free(myargv[i]);
  664.     }
  665.     return 0;
  666. }
  667.  
  668. struct clparms    {
  669.     int nostatus,
  670.         nodelete;
  671.       long *hostsize;
  672.        char name[20] ;         /* Name of remote station */
  673.        char area[64];        /* name of current mail area */
  674.       FILE *mfile;        /* mail data file pointer */
  675. struct let *mbox;
  676.         int nmsgs;        /* number of messages in this mail box */
  677.         int isbbs;
  678. };
  679.  
  680.  
  681. static void
  682. close_notes (a, b, c)
  683. int a;
  684. void *b, *c;
  685. {
  686. struct clparms *cl;
  687. struct    let *cmsg;
  688. char *line;
  689. char tstring[LINELEN], buf[256];
  690. char buf2[256];
  691. long size, diff;
  692. FILE    *nfile;
  693. int i, nextisBID, numwritten = 0;
  694. long msgid;
  695. char *cp;
  696. int foundstatus, firstIDline;
  697.  
  698.     cl = (struct clparms *) b;
  699.     line = tstring;
  700.     sprintf(buf,"%s/CONTROL/%s.ctl",Mailspool,cl->area);
  701.     remove (buf);
  702.  
  703.     fclose (cl->mfile);
  704.     sprintf(buf,"%s/%s.txt",Mailspool,cl->area);
  705.     sprintf(buf2,"%s/%s.bak",Mailspool,cl->area);
  706.     remove (buf2);        /* delete it here, just in case! */
  707.     rename (buf, buf2);
  708.     if ((cl->mfile = subdir_fopen(buf2,READ_TEXT)) == NULLFILE)    {
  709.         rmlock(Mailspool,cl->area);
  710.         free (cl);
  711.         return;
  712.     }
  713.     if ((nfile = subdir_fopen(buf,WRITE_TEXT)) == NULLFILE) {
  714.         fclose(cl->mfile);
  715.         rmlock(Mailspool,cl->area);
  716.         free (cl);
  717.         return;
  718.     }
  719.     /* copy tmp file back to notes file */
  720.     for (cmsg = &cl->mbox[1],i = 1; i <= cl->nmsgs; i++, cmsg++) {
  721.         fseek(cl->mfile,cmsg->start,0);
  722.         pwait (NULL);
  723.         cmsg->start = ftell(nfile);
  724.         size = cmsg->size;
  725.         diff = 0;
  726.         foundstatus = 0;
  727.         /* It is not possible to delete messages if nodelete is set */
  728.         if ((cmsg->status & BM_DELETE) && !cl->nodelete)
  729.             continue;
  730.         nextisBID = 0;
  731.         firstIDline = 0;
  732.         /* copy the header */
  733.         while (size > 0 && fgets(line,LINELEN,cl->mfile) != NULLCHAR) {
  734.             pwait(NULL);  /* can cause problems if exiting NOS */
  735.             if (!firstIDline && nextisBID && (cp=strstr(line,"AA")) != NULLCHAR) {
  736.                 /*what follows is the message-number*/
  737.                 msgid = atol(cp+2);
  738.                 nextisBID = 0;
  739.                 firstIDline = 1;
  740.                 }
  741.             if (!strncmp (line, Hdrs[RECEIVED], strlen(Hdrs[RECEIVED])))
  742.                 nextisBID = 1;
  743.             if (!strncmp (line, Hdrs[STATUS], strlen(Hdrs[STATUS])))
  744.                 foundstatus = 1;
  745.             size -= strlen(line);
  746.             if (*line == '\n') {
  747. #ifdef notagain
  748.                 if (cmsg->status & BM_FORWARDED)    {
  749.                     fprintf(nfile,"%s%s\n",Hdrs[XFORWARD],
  750.                         cl->name);
  751.                     diff += (strlen(Hdrs[XFORWARD]) + strlen(cl->name) + 1);
  752.                 }
  753. #endif
  754.                 if (!foundstatus && (cmsg->status & BM_READ) != 0 && !cl->nostatus)  {
  755.                     fprintf(nfile,"%sR\n",Hdrs[STATUS]);
  756.                     diff += (strlen(Hdrs[STATUS]) + 2);
  757.                 }
  758.                 fprintf(nfile,"\n");
  759.                 break;
  760.             }
  761.             fputs(line,nfile);
  762.             pwait(NULL);  /* can cause problems if exiting NOS */
  763.         }
  764.         pwait (NULL);
  765.         while (size > 0 && fgets(line,LINELEN,cl->mfile) != NULLCHAR) {
  766.             pwait(NULL);  /* can cause problems if exiting NOS */
  767.             if (!cl->nostatus && !strncmp (line, Hdrs[RRECEIPT], strlen(Hdrs[RRECEIPT]))
  768.                 && !strcmp (cl->name, cl->area) && (cmsg->status & BM_RRECEIPT))    {
  769.                     fputs("Return-Receipt-Sent\n", nfile);
  770.                     diff -= strlen(line);
  771.                     diff += 20;
  772.                 }
  773.                 else
  774.                     fputs(line,nfile);
  775.             size -= strlen(line);
  776.             pwait(NULL);   /* dont want no damaged files */
  777.             if (ferror(nfile)) {
  778.                 (void) fclose(nfile);
  779.                 fclose(cl->mfile);
  780.                 rmlock(Mailspool,cl->area);
  781.                 free (cl);
  782.                 return;
  783.             }
  784.         }
  785.         cmsg->status &= BM_READ;
  786.         cmsg->size += diff;
  787.         updateCtl (cl->area, cmsg);
  788.         fflush(nfile);
  789.         numwritten++;
  790.     }
  791.     if (!cl->isbbs && !stricmp(cl->name,cl->area))
  792.         *cl->hostsize = ftell(nfile); /* Update the size of our hosts' mailbox */
  793.                       /* potientially dangerous! */
  794.     /* remove a zero length file */
  795.     if (!numwritten)        {
  796.         (void) fclose(nfile);        
  797.         (void) unlink(buf);
  798.     } else
  799.         (void) fclose(nfile);
  800.     fclose (cl->mfile);
  801.     remove (buf2);        /* leave it around, for now! NOT! */
  802.     rmlock(Mailspool,cl->area);
  803.     free (cl->mbox);
  804.     free (cl);
  805.     pwait(NULL);
  806.     return;
  807. }
  808.  
  809.  
  810.  
  811. /* close the temp file while coping mail back to the mailbox */
  812. int
  813. closenotes(m)
  814. struct mbx *m;
  815. {
  816. int i, nostatus, nodelete = 0;
  817. struct clparms *cl;
  818.  
  819.     if (m->mfile == NULLFILE)
  820.         return 0;
  821.     nostatus = issysarea(m->area);
  822.  
  823.     /* Allow any user to delete from area names starting with 'nts' */
  824.     if(!strnicmp(m->area,"nts",3))
  825.         nostatus = 0;
  826.     if(!m->change || nostatus) {    /* no changes were made (or bulletin) */
  827.         mfclose(m);
  828.         m->mboxsize = 0;
  829.         return 0;
  830.     }
  831.     /* If this area is not our own private message area, then we will not add a
  832.      * Status line to indicate that the message has been read.
  833.      */
  834.     if (strcmp (m->area, m->name))    {
  835.         nostatus = 1;
  836.         /* Don't delete messages from public message areas unless you are
  837.          * a SYSOP.
  838.          */
  839.         nodelete = !(m->privs & SYSOP_CMD);
  840.         }
  841.     /* Allow any user to delete from area names starting with 'nts' */
  842.     if(!strnicmp(m->area,"nts",3))
  843.         nodelete = 0;        
  844.     /* If not a SYSOP, not an "nts" area, not our area, AND deletions, must be BBS forward */
  845.     if (nodelete && (m->change & CHG_DELETE))    {
  846.         nodelete = 0;
  847.         log(-1,"MBOX forwarding rewrite of personal area '%s' by %s ", m->area, m->name);
  848.     }
  849.  
  850.     if(nostatus && nodelete)    {
  851.         mfclose(m);
  852.         m->mboxsize = 0;
  853.         return 0;
  854.     }
  855.     scanmail(m);
  856.     if(lockit(m))
  857.         return -1;
  858.     cl = (struct clparms *) malloc (sizeof (struct clparms));
  859.     cl->nodelete = nodelete;
  860.     cl->nostatus = nostatus;
  861.     cl->hostsize = &m->mysize;
  862.     cl->isbbs = (m->sid & MBX_SID);
  863.     strcpy (cl->name, m->name);
  864.     strcpy (cl->area, m->area);
  865.     cl->mfile = m->mfile;
  866.     cl->nmsgs = m->nmsgs;
  867.     cl->mbox = m->mbox;
  868.     newproc("Closing notefile", 2560, close_notes, 0, cl, 0, 0);
  869.     pwait(NULL);
  870.      m->mfile = NULLFILE;
  871.      m->mbox = (struct let *) 0;
  872.     mfclose(m);
  873.     m->mboxsize = 0;
  874.     return 0;
  875. }
  876.  
  877.  
  878. /* Returns 1 if name is in the given area file, 0 otherwise */
  879. static int
  880. is_area(name, listfile)
  881. char *name, *listfile;
  882. {
  883. char buf[LINELEN], *cp;
  884. FILE *fp;
  885.  
  886.     if((fp = fopen(listfile,READ_TEXT)) == NULLFILE)
  887.         return 0;
  888.     while(fgets(buf,sizeof(buf),fp) != NULLCHAR)     {
  889.         /* The first word on each line is all that matters */
  890.         if(isalnum(buf[0]))     { /* skip comments */
  891.                 if((cp = strpbrk(buf," \t\n")) != NULLCHAR)
  892.                 *cp = '\0';
  893.             nntp_name_expansion (buf);
  894.                 if(stricmp(name,buf) == 0) {    /* found it */
  895.                 fclose(fp);
  896.                 return 1;
  897.                 }
  898.             }
  899.         }
  900.     fclose(fp);
  901.     return 0;
  902. }
  903.  
  904. /* Returns 1 if name is a public message Area, 0 otherwise */
  905. int
  906. isarea(name)
  907. char *name;
  908. {
  909.     return (is_area (name, Arealist));
  910. }
  911.  
  912. /* Returns 1 if name is a public message Area, 0 otherwise */
  913. int
  914. issysarea(name)
  915. char *name;
  916. {
  917.     return (is_area (name, AreaSlist));
  918. }
  919.  
  920. int
  921. lockit(m)
  922. struct mbx *m;
  923. {
  924.     int c, cnt = 0;
  925.  
  926.     while(mlock(Mailspool,m->area)) {
  927.         mspause(1000L/MSPTICK);    /* Wait one second */
  928.         if(++cnt == 10) {
  929.             char buf[80];
  930.             sprintf (buf, "Mail file '%s' is busy, Abort or *Retry ? ", m->area);
  931.             cnt = 0;
  932.             c = tkeywait(buf,1);
  933.             if (c == 'A' || c == 'a' || c == EOF) {
  934.                 mfclose(m);
  935.                 return 1;
  936.             }
  937.         }
  938.     }
  939.     return 0;
  940. }
  941.  
  942. /* read the next message or the current one if new */
  943. int
  944. doreadnext(argc,argv,p)
  945. int argc;
  946. char *argv[];
  947. void *p;
  948. {
  949.     struct mbx *m;
  950.     char buf[10], *newargv[2];
  951.     m = (struct mbx *) p;
  952.     if (m->mfile == NULLFILE)
  953.         return 0;
  954.     if ((m->mbox[m->current].status & BM_READ) != 0) {
  955.         if (m->current == 1 && m->anyread == 0)
  956.             ;
  957.         else if (m->current < m->nmsgs) {
  958.             m->current++;
  959.         } else {
  960.             tprintf("Last message\n");
  961.             return 0;
  962.         }
  963.     }
  964.     sprintf(buf,"%d",m->current);
  965.     newargv[0] = "read";
  966.     newargv[1] = buf;
  967.     return doreadmsg(2,newargv,p);
  968. }
  969.  
  970.  
  971.  
  972. void
  973. leadingCaps (str, others)
  974. char *str;
  975. int others;
  976. {
  977. char *cp;
  978.  
  979.     cp = str;
  980.     strlwr (cp);
  981.     if (!others)    {
  982.         if ((cp = strchr(cp, ' ')) != 0)
  983.             strupr (cp);
  984.         return;
  985.     }
  986.     do    {
  987.         cp = strchr (cp, ' ');
  988.         if (cp != NULL)    {
  989.             cp = skipwhite(cp);
  990.             *cp = toupper (*cp);
  991.         }
  992.     } while (cp);
  993. }
  994.  
  995.  
  996. char *
  997. mblookname (m, str)
  998. struct mbx *m;
  999. char *str;
  1000. {
  1001.  
  1002.     if ((m != NULLMBX) && (m->realname) && (*m->realname == '(') && (m->realname[1] != ')'))
  1003.         return (strdup (m->realname));
  1004.  
  1005.     if (strchr (str, '('))
  1006.         return (0);    /* already has a name */
  1007. #ifdef SAMCALLB
  1008.     return (cb_lookname (str));
  1009. #else
  1010.     return (0);
  1011. #endif
  1012. }
  1013.  
  1014.  
  1015.  
  1016. extern int MbRead;
  1017.  
  1018. /* display message on the crt given msg number */
  1019. /* Modified to allow the 'RM' command, 920307 - WG7J */
  1020. int
  1021. doreadmsg(argc,argv,p)
  1022. int argc;
  1023. char *argv[];
  1024. void *p;
  1025. {
  1026.     struct mbx *m;
  1027.     register int c, col, lin;
  1028.     unsigned char    buf[MAXBUF+2], *cp, *cp2;
  1029.     char *subj = 0, *theto = 0;
  1030.     int msg, cnt, i, usemore=0, verbose, mbxheader, pathcol;
  1031.     int    header, lastheader;
  1032.     long     size;
  1033.     char *myargv[NARG];
  1034.     int myargc;
  1035.     int maxmsg;
  1036.     struct let *cmsg;
  1037.     char *tmpbuf;
  1038.     char *returnreceipt;
  1039. #ifdef USERLOG
  1040.     long msgid;
  1041. #endif
  1042.  
  1043.     m = (struct mbx *) p;
  1044.  
  1045.     /*Check for read-permissions - WG7J */
  1046.     if(m->privs & NO_READCMD) {
  1047.         tputs(Noperm);
  1048.         return 0;
  1049.     }
  1050.     if (m->stype == 'O')
  1051.         return (dombroute (argc, argv, p));
  1052.     if ((m->mfile == NULLFILE) || (m->nmsgs == 0)) {
  1053.         tprintf(Nomail);
  1054.         return 0;
  1055.     }
  1056.     if((lin=m->morerows) != 0)
  1057.         usemore = 1;    /* Display More prompt */
  1058.  
  1059.     /*If this is the RM or VM command, setup myargv[]
  1060.      *to contain up to NARG message numbers - WG7J
  1061.      */
  1062.     if(m->stype == 'M') {
  1063.         myargc = 1;
  1064.         if(!m->newmsgs) {
  1065.             tputs(Nomail);
  1066.             return 0;
  1067.         }
  1068.         /* scan all messsages to find unread ones */
  1069.         maxmsg = min(m->nmsgs,NARG-1);
  1070.         for(i=1;i<=maxmsg;i++){
  1071.             cmsg = &m->mbox[i];
  1072.             if(!(cmsg->status & BM_READ)) { /*found an unread msg!*/
  1073.                 tmpbuf = mallocw(18); /*allocate space for the new argument*/
  1074.                 myargv[myargc++] = itoa(i,tmpbuf,10);
  1075.             }
  1076.         }
  1077.         argc = myargc;
  1078.     } else {
  1079.         /*simply point to the old arguments*/
  1080.         for(i=1;i<argc;i++)
  1081.             myargv[i] = argv[i];
  1082.     }
  1083.     if(argc == 1) {
  1084.         tputs("Usage: Read/Verbose #\n");
  1085.         return 0;
  1086.     }
  1087.     m->state = MBX_READ;
  1088.     for(i = 1; i < argc; ++i) {
  1089.         msg = atoi(myargv[i]);
  1090.         if( msg < 1 || msg > m->nmsgs) {
  1091.             tprintf(Badmsg,msg);
  1092.             goto iamdone;
  1093.         }
  1094.         MbRead++;
  1095.         fseek(m->mfile,m->mbox[msg].start,0);
  1096. #ifdef USERLOG
  1097.         /* Check the ID number of this message and
  1098.          * adjust new lastread count, if needed - WG7J
  1099.          */
  1100.         fgets(buf,MAXBUF+2,m->mfile);    /* the 'From ' line */
  1101.         fgets(buf,MAXBUF+2,m->mfile);    /* the 'Received: ' line */
  1102.         fgets(buf,MAXBUF+2,m->mfile);    /* the ' ID' line */
  1103.         /* find id number */
  1104.         if((cp=strstr(buf,"AA")) != NULLCHAR) {
  1105.         /*what follows is the message-number*/
  1106.             msgid = atol(cp+2);
  1107.         if((msgid > m->lastread) && (msgid > m->newlastread))
  1108.                 m->newlastread = msgid;
  1109.             }
  1110.         fseek(m->mfile,m->mbox[msg].start,0);
  1111. #endif
  1112. #ifdef nope /* was USERLOG */
  1113.             if((m->mbox[msg].bid > m->lastread) && (m->mbox[msg].bid > m->newlastread))
  1114.                 m->newlastread = m->mbox[msg].bid;
  1115. #endif
  1116.  
  1117.     m->anyread = 1;
  1118.     returnreceipt = 0;
  1119.         size = m->mbox[msg].size;
  1120.         m->current = msg;
  1121.         header = NOHEADER;
  1122.         mbxheader = 0;
  1123.         if((*argv[0] == 'v') || (m->stype == 'H'))
  1124.             verbose = 1;    /* display all header lines */
  1125.         else
  1126.             verbose = 0;
  1127.  
  1128.     m->inmessage = 1;
  1129.     if(m->stype != 'M')
  1130.         bbscolorcls (m);
  1131.     bbscolorchange (m, "07");
  1132.         tprintf("Message #%d %s\n", msg,
  1133.             m->mbox[msg].status & BM_ONHOLD ? "[On Hold - Awaiting Review of SYSOP]" :
  1134.             m->mbox[msg].status & BM_DELETE ? "[Deleted]" : "");
  1135.  
  1136.     if ((m->mbox[msg].status & (BM_ONHOLD | BM_DELETE)) && !(m->privs & SYSOP_CMD))
  1137.             continue;
  1138.  
  1139.         /* When you have sysop privs,
  1140.          * only mark your own private area as read and changed.
  1141.          * other areas, only mark as read, NOT changed !
  1142.          * for regular users, simply mark all as read and changed.
  1143.          * That way sysops can read other's mail without
  1144.          * marking stuff read that really wasn't read by
  1145.          * the right person !
  1146.          * for regular users, simply mark as read.
  1147.          * 910312 - WG7J
  1148.          */
  1149.         if(!(m->mbox[msg].status & BM_READ)) {
  1150.             m->mbox[msg].status |= BM_READ;
  1151.                 /* regular users */
  1152.             if(!issysarea (m->area) && (!(m->privs & SYSOP_CMD) || \
  1153.                ((m->privs & SYSOP_CMD) && !strcmp(m->name,m->area)))) /*sysops*/
  1154.             m->change |= CHG_READ;
  1155.             m->newmsgs--;
  1156.         }
  1157.         --lin;
  1158.         col = 0;
  1159.         while (!feof(m->mfile) && size > 0) {
  1160.             for (col = 0;  col < MAXBUF;) {
  1161.                 c = getc(m->mfile);
  1162.                 size--;
  1163.                 if (feof(m->mfile) || size == 0) /* end this line */
  1164.                     break;
  1165.                 if (c == '\t') {
  1166.                     cnt = col + 8 - (col & 7);
  1167.                     if (cnt >= MAXBUF) /* end this line */
  1168.                         break;
  1169.                     while (col < cnt)
  1170.                         buf[col++] = ' ';
  1171.                 } else {
  1172.                     if (c == '\n')
  1173.                         break;
  1174.                     buf[col++] = c;
  1175.                 }
  1176.             }
  1177.             if(col < MAXBUF)
  1178.                 buf[col++] = '\n';
  1179.             buf[col] = '\0';
  1180.             if(mbxheader > 0) {
  1181.                  /* Digest R: lines and display as a Path: line */
  1182.                  if(strnicmp(buf,"R:",2) != 0 ||
  1183.                 (cp = strchr(buf,'@')) == NULLCHAR) {
  1184.                   tputc('\n');
  1185.                   bbscolorchange (m, "0B");
  1186.                   mbxheader = -1; /* don't get here again */
  1187.                   verbose = 1;
  1188.                  }
  1189.                  else {
  1190.                   if(*(++cp) == ':')
  1191.                        ++cp;
  1192.                   for(cp2 = cp; isalnum(*cp2); ++cp2)  ;
  1193.                   *cp2 = '\0';
  1194.                   if(mbxheader++ == 1) {
  1195.                       bbscolorchange (m, "07");
  1196.                                tputs(Hdrs[PATH]);
  1197.                        pathcol = 5;
  1198.                        --lin;
  1199.                   }
  1200.                   else {
  1201.                        tputc('!');
  1202.                        if(++pathcol + strlen(cp) > MAXCOL-3){
  1203.                         tputs("\n      ");
  1204.                         pathcol = 5;
  1205.                         --lin;
  1206.                        }
  1207.                   }
  1208.                   tputs(cp);
  1209.                   pathcol += strlen(cp);
  1210.                   ++lin;    /* to allow for not printing it later */
  1211.                  }
  1212.             }
  1213.             if(col == 1 && !mbxheader)    {
  1214.                  bbscolorchange (m, "0B");
  1215.                  /* last header line reached */
  1216.                  if (!verbose)
  1217.                      mbxheader = 1;
  1218.             }
  1219.             if(verbose)
  1220. /*                tputs(buf);    */
  1221.                 colorprintf (NULLCHAR, m->usecolor, buf);
  1222.             if (!strncmp (Hdrs[RRECEIPT], buf, strlen(Hdrs[RRECEIPT])))
  1223.                 returnreceipt = strdup(&buf[strlen(Hdrs[RRECEIPT])]);
  1224.             if(!verbose && !mbxheader){
  1225.                 lastheader = header;
  1226.                 if(!isspace(*buf))
  1227.                     header = htype(buf);
  1228.                 else
  1229.                     header = lastheader;
  1230.                 switch(header) {
  1231.                 case SUBJECT:    subj = strdup (&buf[strlen(Hdrs[SUBJECT]) - 4]);
  1232.                         tputs(buf);
  1233.                         break;
  1234.                 case MSGID:    /* give a "traditional BBS" version to the user */
  1235.                         {
  1236.                         char bid[30];
  1237.  
  1238.                             makeBBSbid (bid, buf, 1);
  1239.                             strupr (bid);
  1240.                             tprintf ("%s%s\n", Hdrs[MSGID], bid);
  1241.                         }
  1242.                         break;
  1243.                 case TO:        theto = strdup (&buf[strlen(Hdrs[TO])]);
  1244.                 case FROM:    {
  1245.                         char DFAR *nm;
  1246.                             rip (buf);
  1247.                             tputs (buf);
  1248.                             nm = strchr (buf, ' ');
  1249.                             if ((nm = mblookname (NULLMBX, ++nm)) != 0)    {
  1250.                                 tputs (nm);
  1251.                                 free (nm);
  1252.                             }
  1253.                             tputc ('\n');
  1254.                         }
  1255.                         break;
  1256.                 case CC:
  1257.                 case DATE:
  1258.                 case REPLYTO:
  1259.                 case APPARTO:
  1260.                 case ERRORSTO:
  1261.                 case XMAILGROUP:
  1262.                 case ORGANIZATION:
  1263.                     tputs(buf);
  1264.                     break;
  1265.                 default:
  1266.                     ++lin;
  1267.                 }
  1268.             }
  1269.             col = 0;
  1270.             if(usemore && --lin == 0){
  1271.                 if(m->type == TELNET || m->type == TIP)
  1272.                     c = tkeywait("--More--",0);
  1273.                 else  /* For AX.25 and NET/ROM connects - WG7J */
  1274.                     c = mykeywait(anymore, m);
  1275. #ifdef MBXTDISC
  1276.         start_timer(&m->tdisc);
  1277. #endif
  1278.                 lin = m->morerows;
  1279.                 if(c == -1 || c == 'q' || c == 'Q')
  1280.                     break;
  1281. #ifdef TNOS_68K
  1282.                 if(c == '\l' || c == '\r')
  1283. #else
  1284.                 if(c == '\n' || c == '\r')
  1285. #endif
  1286.                     lin = 1;
  1287.             }
  1288.         }
  1289.     if (returnreceipt && !(m->mbox[msg].status & BM_RRECEIPT))    {
  1290.         struct mbx *mm;
  1291.         m->mbox[msg].status |= BM_RRECEIPT;
  1292.         rip (returnreceipt);    /* strip off <CR> */
  1293.         tprintf ("[Sending return receipt to '%s']\n", returnreceipt);
  1294.         mm = (struct mbx *) malloc (sizeof (struct mbx));
  1295.         mm->tomsgid = mm->date = mm->origbbs = mm->tofrom = NULLCHAR;
  1296.         mm->origto = returnreceipt;
  1297.         mm->subject = subj;
  1298.         mm->realname = "()";
  1299.         rip (subj);    /* strip off <CR> */
  1300.         subj[0] = subj[1] = 'R';
  1301.         subj[2] = ':';
  1302.         subj[3] = ' ';
  1303.         mm->stype = 'P';
  1304.         strcpy (mm->name, m->name);
  1305.         if ((mm->tfile = tmpfile()) != NULLFILE)    {
  1306.             struct list *cclist = NULLLIST;
  1307.             char fullfrom[MBXLINE];
  1308.             addlist(&cclist, returnreceipt, 0, returnreceipt);
  1309.             sprintf (fullfrom, "%s@%s", m->name, Hostname);
  1310.             mbx_data (mm, NULLLIST, NULLCHAR, 0);
  1311.             rip (theto);    /* strip off the <CR> */
  1312.             fprintf (mm->tfile, "Message received - Originally addressed to: '%s'\n", theto);
  1313.             fseek(mm->tfile, 0L, 0);    /* rewind file */
  1314.             queuejob (mm->tfile, Hostname, cclist, fullfrom);
  1315.             fclose (mm->tfile);
  1316.             del_list(cclist);
  1317.             smtptick(NULL);        /* wake up SMTP to send mail */
  1318.             }
  1319.         free (mm);
  1320.         free(returnreceipt);
  1321.         }
  1322.     free (subj);
  1323.     free (theto);
  1324.     m->inmessage = 0;
  1325.     }
  1326. iamdone:
  1327.     /* If this was 'RM' or 'VM',
  1328.      * free the memory allocated for myargv[] - WG7J
  1329.      */
  1330.     if(m->stype == 'M') {
  1331.         for(i=1;i<argc;i++)
  1332.             free(myargv[i]);
  1333.     }
  1334.     return 0;
  1335. }
  1336.  
  1337. /* Set up m->to when replying to a message. The subject is returned in
  1338.  * m->line.
  1339.  */
  1340. int
  1341. mbx_reply(argc,argv,m,cclist,rhdr)
  1342. int argc;
  1343. char *argv[];
  1344. struct mbx *m;
  1345. struct list **cclist;    /* Pointer to buffer for pointers to cc recipients */
  1346. char **rhdr;        /* Pointer to buffer for extra reply headers */
  1347. {
  1348. char subject[MBXLINE], *msgid = NULLCHAR, *date = NULLCHAR;
  1349. char *cp;
  1350. int msg, lastheader, header = NOHEADER;
  1351. long size;
  1352.  
  1353.     /* Free anything that might be allocated
  1354.      * since the last call to mbx_to() or mbx_reply()
  1355.      */
  1356.     free(m->to);
  1357.     m->to = NULLCHAR;
  1358.     free(m->tofrom);
  1359.     m->tofrom = NULLCHAR;
  1360.     free(m->tomsgid);
  1361.     m->tomsgid = NULLCHAR;
  1362.     free(m->origto);
  1363.     m->origto = NULLCHAR;
  1364.     subject[0] = '\0';
  1365.  
  1366.     if(argc == 1)
  1367.          msg = m->current;
  1368.     else
  1369.          msg = atoi(argv[1]);
  1370.     if (m->mfile == NULLFILE) {
  1371.          if(m->sid & MBX_SID)
  1372.           tputs("NO - ");
  1373.         tputs(Nomail);
  1374.         return 0;
  1375.     }
  1376.     if(msg < 1 || msg > m->nmsgs) {
  1377.          if(m->sid & MBX_SID)
  1378.           tputs("NO - ");
  1379.          tputs(Badmsg);
  1380.          return -1;
  1381.     }
  1382.     fseek(m->mfile,m->mbox[msg].start,0);
  1383.     size = m->mbox[msg].size;
  1384.     m->current = msg;
  1385.     while(size > 0 && fgets(m->line,MBXLINE-1,m->mfile) != NULLCHAR) {
  1386.          size -= strlen(m->line);
  1387.          if(m->line[0] == '\n')    /* end of header */
  1388.           break;
  1389.          rip(m->line);
  1390.          lastheader = header;
  1391.          if(!isspace(m->line[0])) {
  1392.           header = htype(m->line);
  1393.           lastheader = NOHEADER;
  1394.          }
  1395.          switch(header) {
  1396.          case SUBJECT:
  1397.           if(strlen(m->line) > 11 && !strnicmp(&m->line[9],"Re:",3))
  1398.                strcpy(subject,&m->line[9]);
  1399.           else
  1400.                sprintf(subject,"Re: %s",&m->line[9]);
  1401.           break;
  1402.          case FROM:
  1403.           if(m->to == NULLCHAR && (cp = getaddress(m->line,0)) !=
  1404.              NULLCHAR)
  1405.                m->to = strdup(cp);
  1406.           break;
  1407.          case REPLYTO:
  1408.           if((cp = getaddress(m->line,0)) != NULLCHAR) {
  1409.                free(m->to);
  1410.                m->to = strdup(cp);
  1411.           }
  1412.           break;
  1413.          case MSGID:
  1414.           free(msgid);
  1415.           msgid = strdup(&m->line[12]);
  1416.           break;
  1417.          case DATE:
  1418.           free(date);
  1419.           date = strdup(&m->line[6]);
  1420.           break;
  1421. #ifdef notdef
  1422.          /* don't want a reply back to myself - WG7J */
  1423.          case TO:
  1424.          case APPARTO:
  1425. #endif
  1426.          case CC:
  1427.           /* Get addresses on To, Cc and Apparently-To lines */
  1428.           cp = m->line;
  1429.           m->line[strlen(cp)+1] = '\0';    /* add extra null at end */
  1430.           for(;;) {
  1431.                if((cp = getaddress(cp,lastheader == header ||
  1432.                cp != m->line)) == NULLCHAR)
  1433.                     break;
  1434.                addlist(cclist,cp,0,cp);
  1435.                /* skip to next address, if any */
  1436.                cp += strlen(cp) + 1;
  1437.           }
  1438.           break;
  1439.          }
  1440.     }
  1441.     if(msgid != NULLCHAR || date != NULLCHAR) {
  1442.          *rhdr = mallocw(LINELEN);
  1443.          sprintf(*rhdr,"In-Reply-To: your message ");
  1444.          if(date != NULLCHAR) {
  1445.           sprintf(m->line,"of %s.\n",date);
  1446.           strcat(*rhdr,m->line);
  1447.           if(msgid != NULLCHAR)
  1448.                strcat(*rhdr,"             ");
  1449.          }
  1450.          if(msgid != NULLCHAR) {
  1451.           sprintf(m->line,"%s\n",msgid);
  1452.           strcat(*rhdr,m->line);
  1453.          }
  1454.          free(msgid);
  1455.          free(date);
  1456.     }
  1457.     strcpy(m->line,subject);
  1458.     return 0;
  1459. }
  1460.  
  1461. #ifdef USERLOG
  1462.  
  1463. /*get the last message listed/read
  1464.  *from the areaname.USR file
  1465.  *keeps track for each user.
  1466.  *February '92, WG7J
  1467.  */
  1468. void
  1469. getlastread(m)
  1470. struct mbx *m;
  1471. {
  1472.     FILE *Alog;
  1473.     char buf[256];
  1474.     char *cp;
  1475.     int found=0;
  1476.  
  1477.     m->lastread = m->newlastread = 0L;
  1478.  
  1479.     sprintf(buf,"%s/USERS/%s.usr",Mailspool,m->area);
  1480.     if ((Alog = subdir_fopen(buf,"r+")) == NULLFILE) {
  1481.         /* USR file doesn't exist, create it */
  1482.         if((Alog = subdir_fopen(buf,"w")) == NULLFILE)
  1483.             return;
  1484.         /* Add this user as first one */
  1485.         sprintf(buf,"%s 0\n",m->name);
  1486.         fputs(buf,Alog);
  1487.         fclose(Alog);
  1488.         return;
  1489.     }
  1490.     /*Find user in the usr file for this area*/
  1491.     for(;;) {
  1492.         if(fgets(buf,sizeof(buf),Alog) == NULLCHAR)
  1493.             break;
  1494.         if((cp=strchr(buf,' ')) != NULLCHAR)
  1495.             *cp = '\0';
  1496.         if(!stricmp(m->name,buf)) {
  1497.             /*found user*/
  1498.             cp++;
  1499.             while(*cp == ' ')   /*skip blanks*/
  1500.                 cp++;
  1501.             m->lastread = atol(cp);
  1502.             found = 1;
  1503.             break;
  1504.         }
  1505.     }
  1506.     if(!found) {
  1507.         /*Add user*/
  1508.         sprintf(buf,"%s 0\n",m->name);
  1509.         fputs(buf,Alog);
  1510.     }
  1511.     fclose(Alog);
  1512.     return;
  1513. }
  1514.  
  1515. /* Write the new last read id number to the USR file - WG7J
  1516.  * only update if this is not a bbs,
  1517.  * current area is a public area and not 'help',
  1518.  * or anything that starts with 'sys',
  1519.  * and a new message was actually listed/read
  1520.  */
  1521. void
  1522. setlastread(m)
  1523. struct mbx *m;
  1524. {
  1525. FILE *Alog, *tfile;
  1526. char buf[256];
  1527. char tmpname[80];
  1528. char *cp;
  1529. int doit = 0;
  1530.  
  1531.     if((m->sid & MBX_SID) || (m->newlastread <= m->lastread) )
  1532.         return;
  1533.  
  1534. /*    if(issysarea(m->area))
  1535.         if ((m->privs & MBX_SYSOP) || (stricmp(m->area, "help")))
  1536.             doit = 1;    */
  1537.       if(isarea(m->area) && strcmp(m->area, "help"))
  1538.             doit = 1;
  1539.       if ((m->privs & SYSOP_CMD) && issysarea(m->area))
  1540.             doit = 1;
  1541.  
  1542.       if (doit)        {
  1543.  
  1544. #ifdef notdef
  1545.     tprintf("SETLAST: %d\n",m->newlastread);
  1546. #endif
  1547.  
  1548.  
  1549.         sprintf(buf,"%s/USERS/%s.usr",Mailspool,m->area);
  1550.  
  1551.         /* Rename the USR file to a tempfile */
  1552.         tmpnam(tmpname);
  1553.         if(rename(buf,tmpname))
  1554.             /* Can't rename ??? */
  1555.             return;
  1556.  
  1557.         if((Alog = subdir_fopen(buf,"w")) == NULLFILE) {
  1558.             /* can't creat new USR file ???*/
  1559.             rename(tmpname,buf);    /* try to undo the damage */
  1560.             return;
  1561.         }
  1562.  
  1563.         if((tfile = subdir_fopen(tmpname,"r")) == NULLFILE)
  1564.             /* can't open renamed file ??? */
  1565.             return;
  1566.  
  1567.         /*Write all users back, but update this one!*/
  1568.         while(fgets(buf,sizeof(buf),tfile) != NULLCHAR) {
  1569.             if((cp=strchr(buf,' ')) != NULLCHAR)
  1570.                 *cp = '\0';
  1571.             if(!stricmp(m->name,buf)) {
  1572.                 /*found this user*/
  1573.                 sprintf(buf,"%s %lu\n",m->name,m->newlastread);
  1574.             } else
  1575.                 *cp = ' '; /* restore the space !*/
  1576.             fputs(buf,Alog);
  1577.         }
  1578.         fclose(tfile);
  1579.         unlink(tmpname);
  1580.         fclose(Alog);
  1581.     }
  1582.     return;
  1583. }
  1584.  
  1585. #endif /*USERLOG*/
  1586.  
  1587. void
  1588. scanmail(m)         /* Get any new mail */
  1589. struct mbx *m;
  1590. {
  1591. long diff;
  1592.  
  1593.     if ((diff = isnewmail(m)) == 0L)
  1594.         return;
  1595.     if(lockit(m))
  1596.         return;
  1597.     initnotes(m);
  1598.     rmlock(Mailspool,m->area);
  1599. }
  1600.  
  1601. /* Check the current mailbox to see if new mail has arrived.
  1602.  * Returns the difference in size.
  1603.  */
  1604. static long
  1605. isnewmail(m)
  1606. struct mbx *m;
  1607. {
  1608. char buf[256];
  1609.  
  1610.     sprintf(buf,"%s/%s.txt",Mailspool,m->area);
  1611.     return fsize(buf) - m->mboxsize;
  1612. }
  1613.  
  1614. /* Check if the private mail area has changed */
  1615. long
  1616. isnewprivmail(m)
  1617. struct mbx *m;
  1618. {
  1619. long cnt;
  1620. char buf[256];
  1621.  
  1622.     sprintf(buf,"%s/%s.txt",Mailspool,m->name);
  1623.     cnt = m->mysize;
  1624.     m->mysize = fsize(buf);
  1625.     return m->mysize - cnt; /* != 0 not more than once */
  1626. }
  1627.  
  1628.  
  1629. void
  1630. notifynewmail (m)
  1631. struct mbx *m;
  1632. {    /* not a BBS, but from within ANY area */
  1633.     if (!(m->sid & MBX_SID) && (isnewprivmail(m) > 0L))    {
  1634.         tprintf("\007You have new mail in your personal area (%s). Please Kill when read!\n", m->name);
  1635.         usflush(Curproc->output);
  1636.     }
  1637. }
  1638.  
  1639. #endif
  1640.  
  1641. /* This function returns the length of a file. The proper thing would be
  1642.  * to use stat(), but it fails when using DesqView together with Turbo-C
  1643.  * code.
  1644.  */
  1645. long
  1646. fsize(name)
  1647. char *name;
  1648. {
  1649. long cnt;
  1650. FILE *fp;
  1651.  
  1652.     if((fp = subdir_fopen(name,READ_TEXT)) == NULLFILE)
  1653.         return -1L;
  1654.     fseek(fp,0L,2);
  1655.     cnt = ftell(fp);
  1656. /*    cnt = filelength (fileno(fp)); */
  1657.     fclose(fp);
  1658.     return cnt;
  1659. }
  1660.  
  1661. /* close the temporary mail file */
  1662. static void
  1663. mfclose(m)
  1664. struct mbx *m;
  1665. {
  1666.     if(m->mfile != NULLFILE)
  1667.         fclose(m->mfile);
  1668.     m->mfile = NULLFILE;
  1669. #ifdef SETVBUF
  1670.     free(m->stdoutbuf);
  1671.     m->stdoutbuf = NULLCHAR;
  1672. #endif
  1673. }
  1674.  
  1675.  
  1676. /* erase a previous prompt via backspaces */
  1677. void
  1678. backEmUp (len)
  1679. int len;
  1680. {
  1681. int i;
  1682.     /* Get rid of the prompt */
  1683.     for(i=len;i != 0;i--)
  1684.         tputc('\b');
  1685.     for(i=len;i != 0;i--)
  1686.         tputc(' ');
  1687.     for(i=len;i != 0;i--)
  1688.         tputc('\b');
  1689.     tflush ();
  1690. }
  1691.  
  1692.  
  1693. /* Print prompt and read one character, telnet version */
  1694. int
  1695. tkeywait(prompt,flush)
  1696. char *prompt;    /* Optional prompt */
  1697. int flush;    /* Flush queued input? */
  1698. {
  1699.     int c, oldimode,oldomode;
  1700.  
  1701.     if(flush && socklen(Curproc->input,0) != 0)
  1702.         recv_mbuf(Curproc->input,NULL,0,NULLCHAR,0); /* flush */
  1703.     if(prompt == NULLCHAR)
  1704.         prompt = "Hit enter to continue"; 
  1705.     tprintf("%s%c%c%c",prompt,IAC,WILL,TN_ECHO);
  1706.     usflush(Curproc->output);
  1707.  
  1708.     /* discard the response */
  1709.  
  1710.     oldimode = sockmode(Curproc->input,SOCK_BINARY);
  1711.     oldomode = sockmode(Curproc->output,SOCK_BINARY);
  1712.  
  1713.     while((c = rrecvchar(Curproc->input)) == IAC){
  1714.         c = rrecvchar(Curproc->input);
  1715.         if(c > 250 && c < 255)
  1716.             rrecvchar(Curproc->input);
  1717.     }
  1718.  
  1719.     sockmode(Curproc->output,oldomode);
  1720.     sockmode(Curproc->input,oldimode);
  1721.  
  1722.     backEmUp (strlen(prompt));
  1723.     tprintf("%c%c%c",IAC,WONT,TN_ECHO);
  1724.     usflush(Curproc->output);
  1725.     return c;
  1726. }
  1727.  
  1728. /* Print -more- prompt and read reply,
  1729.  * AX.25 and NETROM version - WG7J
  1730.  * <CR> is taken as a 'Yes'
  1731.  */
  1732. int
  1733. mykeywait(prompt,m)
  1734. char *prompt;
  1735. struct mbx *m;
  1736. {
  1737.     tputs(prompt);
  1738.     usflush(Curproc->output);
  1739.     if(recvline(m->user, m->line, MBXLINE) == -1) {
  1740.         return -1;
  1741.     }
  1742.     /* Only 'N' or 'n' really matters */
  1743.     if((*m->line == 'N') || (*m->line == 'n'))
  1744.         return -1;
  1745.     return 0;
  1746. }
  1747.